/*
 * collisions.h
 *
 *  Created on: Dec 17, 2011
 *      Author: armandoperico
 */

#ifndef COLLISIONS_H_
#define COLLISIONS_H_

Octree* _octree;
vector<CellPair*> _tobeMerged;
float MIN_BALL_SIZE = 0.5;

vector<CellPair*> getToBeMergedCells(){
	return _tobeMerged;
}

Octree* getOctree(){
	return _octree;
}

void potentialCellCellCollisions(vector<CellPair> &potentialCollisions, vector<Cell*> &Cells, Octree* octree) {
	octree->potentialCellCellCollisions(potentialCollisions);
}
void potentialCellWallCollisions(vector<CellWallPair> &potentialCollisions, vector<Cell*> &Cells, Octree* octree) {
	octree->potentialCellWallCollisions(potentialCollisions);
}

int _find_cell_position(vector<Cell*> &cells, Cell *b1){
	for(unsigned int i = 0; i < cells.size(); i++)
		if(b1 == cells[i]) return i;
	return -1;
}

bool testCellCellCollision(Cell* b1, Cell* b2) {
	//Check whether the Cells are close enough
	float r = b1->r + b2->r;
	if ((b1->pos - b2->pos).magnitudeSquared() < r * r) {
		//Check whether the Cells are moving toward each other
		Vec3f netVelocity = b1->v - b2->v;
		Vec3f displacement = b1->pos - b2->pos;
		return netVelocity.dot(displacement) < 0;
	}else{
		return false;
	}
}

Cell* _get_max_Cell(vector<Cell*> &Cells){
	Cell* max_Cell = Cells[0];
	for(unsigned int i = 1; i < Cells.size(); i++) {
		if(Cells[i]->r > max_Cell->r)
			max_Cell = Cells[i];
	}
	return max_Cell;
}

bool check_end_of_game(vector<Cell*> &cells){
	Cell * b1 = cells[0];
	Cell * b2 = cells[1];
	return (b1->r >= 2*b2->r);
}

void handle_merging() {
//	advance(_cells,mainCell, _octree, (float)TIMER_MS / 19000.0f, _timeUntilUpdate);
	if(_tobeMerged.size() == 0) return;

	for(unsigned int i = 0; i < _tobeMerged.size(); i++) {
		Cell* ball1 = _tobeMerged[i]->cell1;
		Cell* ball2 = _tobeMerged[i]->cell2;
		if(ball1->r >= MIN_BALL_SIZE){
			ball1->r -= ball1->r/0.01;
			ball2->r += ball1->r/0.01;
		}else{
			if(i < _tobeMerged.size() ){
				cout <<"removing cell \n";
				//_tobeMerged.erase(_tobeMerged.begin()+i );
				//vector<CellPair*>::iterator f = find(_tobeMerged.begin(), _tobeMerged.end(), _tobeMerged[i]);
			//	if( f != _tobeMerged.end() ){
					//_tobeMerged.erase(f);
					_tobeMerged.erase(_tobeMerged.begin()+i, _tobeMerged.begin()+1+i);
				//}
			}
		}
	}
	glutPostRedisplay();
	//glutTimerFunc(TIMER_MS, handle_merging);
}


// merges b1 into b2
void merge(vector<Cell*> &cells, Cell* mainCell, Cell*b1, Cell*b2){
	b1->color = Vec3f(0.6f * randomFloat() + 0.2f, 0.6f * randomFloat() + 0.2f, 0.6f * randomFloat() + 0.2f);

	CellPair * mergeCouple = new CellPair();
	mergeCouple->cell1=b1;
	mergeCouple->cell2=b2;

	if(b1 == mainCell){
		cout << "----------------------------------------------\n";
		cout << "------------------GAME OVER-------------------\n";
		cout << "----------------------------------------------\n";
		exit(0);
	}

	unsigned int cell_pos = _find_cell_position(cells, b1);
	if( cell_pos >= 0 && cell_pos < cells.size() ){
		b2->r += (b1->r)/3;
		cells.erase (cells.begin()+cell_pos);
	}
	// adding it to the tobemerged array (used afterwords)
	_tobeMerged.push_back(mergeCouple);


	vector<Cell*>::iterator f = find(cells.begin(), cells.end(), b1);
	if( f != cells.end() ) cells.erase(f);

	// removing the ball to be merged from the main cells list
//	unsigned int ball_pos = _find_ball_position(balls, b1);
//	if( ball_pos >= 0 && ball_pos < balls.size() )
//		balls.erase(balls.begin() + ball_pos);

	//_tobeMerged.push_back(mergeCouple);
	//delete _Cells[i];
}


void reflect_cells(Cell* b1, Cell* b2){
	Vec3f displacement = (b1->pos - b2->pos).normalize();
	b1->v -= 2 * displacement * b1->v.dot(displacement);
	b2->v -= 2 * displacement * b2->v.dot(displacement);
}

//Handles all Cell-Cell collisions
void handleCellCellCollisions(vector<Cell*> &cells, Cell* mainCell, Octree* octree) {
	vector<CellPair> bps;
	potentialCellCellCollisions(bps, cells, octree);
	for(unsigned int i = 0; i < bps.size(); i++) {
		CellPair bp = bps[i];

		Cell* b1 = bp.cell1;
		Cell* b2 = bp.cell2;
		if (testCellCellCollision(b1, b2)) {
			if(b1->r > b2->r){ // b2 merges b1
				merge(cells, mainCell, b2, b1);
				reflect_cells(b1,b2);
			}else if(b1->r < b2->r){ // b1 merges b2
				merge(cells, mainCell, b1, b2);
				reflect_cells(b1,b2);
			}else{
				reflect_cells(b1,b2);
			}
		}
	}
}

//Returns the direction from the origin to the wall
Vec3f wallDirection(Wall wall) {
	switch (wall) {
		case WALL_LEFT:
			return Vec3f(-1, 0, 0);
		case WALL_RIGHT:
			return Vec3f(1, 0, 0);
		case WALL_FAR:
			return Vec3f(0, 0, -1);
		case WALL_NEAR:
			return Vec3f(0, 0, 1);
		case WALL_TOP:
			return Vec3f(0, 1, 0);
		case WALL_BOTTOM:
			return Vec3f(0, -1, 0);
		default:
			return Vec3f(0, 0, 0);
	}
}

//Returns whether a Cell and a wall are colliding
bool testCellWallCollision(Cell* Cell, Wall wall) {
	Vec3f dir = wallDirection(wall);
	//Check whether the Cell is far enough in the "dir" direction, and whether
	//it is moving toward the wall
	return Cell->pos.dot(dir) + Cell->r > BOX_SIZE / 2 && Cell->v.dot(dir) > 0;
}

//Handles all Cell-wall collisions
void handleCellWallCollisions(vector<Cell*> &Cells, Octree* octree) {
	vector<CellWallPair> bwps;
	potentialCellWallCollisions(bwps, Cells, octree);
	for(unsigned int i = 0; i < bwps.size(); i++) {
		CellWallPair bwp = bwps[i];

		Cell* b = bwp.cell;
		Wall w = bwp.wall;
		if (testCellWallCollision(b, w)) {
			//Make the Cell reflect off of the wall
			Vec3f dir = (wallDirection(w)).normalize();
			b->v -= 2 * dir * b->v.dot(dir);
		}
	}
}


void checkCellsColors(Cell * mainCell,vector<Cell*> &cells){
	for(unsigned int i = 0; i < cells.size(); i++) {
		if(cells[i]==mainCell) continue;
		if(cells[i]->r > mainCell->r){
			cells[i]->color =Vec3f(1.0f,0.1f,0.1f);
		}else{
			cells[i]->color =Vec3f(0.1f,1.0f,0.1f);
		}
	}
}

#endif /* COLLISIONS_H_ */
